home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / checkbox / contrib / persist.py < prev    next >
Encoding:
Python Source  |  2009-04-27  |  17.1 KB  |  543 lines

  1. # Copyright (c) 2006 Canonical
  2. # Copyright (c) 2004 Conectiva, Inc.
  3. #
  4. # Written by Gustavo Niemeyer <gustavo@niemeyer.net>
  5. #
  6. # This Python module is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as published
  8. # by the Free Software Foundation; either version 2 of the License, or (at
  9. # your option) any later version.
  10. #
  11. # This Python module is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14. # General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this Python module; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19. #
  20. import os
  21. import sys
  22. import copy
  23. import re
  24. import posixpath
  25.  
  26.  
  27. __all__ = ["Persist", "PickleBackend", "BPickleBackend",
  28.     "path_string_to_tuple", "path_tuple_to_string", "RootedPersist",
  29.     "PersistError", "PersistReadOnlyError"]
  30.  
  31.  
  32. NOTHING = object()
  33.  
  34.  
  35. class PersistError(Exception):
  36.     pass
  37.  
  38.  
  39. class PersistReadOnlyError(PersistError):
  40.     pass
  41.  
  42.  
  43. class Persist(object):
  44.     """Persistence handler.
  45.  
  46.     There are three different kinds of opition maps, regarding the
  47.     persistence and priority that maps are queried.
  48.  
  49.     hard - Options are persistent.
  50.     soft - Options are not persistent, and have a higher priority
  51.            than persistent options.
  52.     weak - Options are not persistent, and have a lower priority
  53.            than persistent options.
  54.  
  55.     @ivar filename: The name of the file where persist data is saved
  56.         or None if not filename is available.
  57.     """
  58.  
  59.  
  60.     def __init__(self, filename=None, backend=None):
  61.         """
  62.         @param backend: The backend to use. If none is specified,
  63.             L{BPickleBackend} will be used.
  64.         @param filename: The default filename to save to and load from. If
  65.             specified, and the file exists, it will be immediately
  66.             loaded. Specifying this will also allow L{save} to be called
  67.             without any arguments to save the persist.
  68.         """
  69.         if backend is None:
  70.             backend = BPickleBackend()
  71.         self._backend = backend
  72.         self._hardmap = backend.new()
  73.         self._softmap = {}
  74.         self._weakmap = {}
  75.         self._readonly = False
  76.         self._modified = False
  77.         self.filename = filename
  78.         if filename is not None and posixpath.exists(filename):
  79.             self.load(filename)
  80.  
  81.     def _get_readonly(self):
  82.         return self._readonly
  83.  
  84.     def _set_readonly(self, flag):
  85.         self._readonly = bool(flag)
  86.  
  87.     def _get_modified(self):
  88.         return self._modified
  89.  
  90.     readonly = property(_get_readonly, _set_readonly)
  91.     modified = property(_get_modified)
  92.  
  93.     def reset_modified(self):
  94.         self._modified = False
  95.  
  96.     def assert_writable(self):
  97.         if self._readonly:
  98.             raise PersistReadOnlyError("Configuration is in readonly mode.")
  99.  
  100.     def load(self, filepath):
  101.         filepath = posixpath.expanduser(filepath)
  102.         if not posixpath.isfile(filepath):
  103.             raise PersistError("File not found: %s" % filepath)
  104.         if posixpath.getsize(filepath) == 0:
  105.             return
  106.         try:
  107.             self._hardmap = self._backend.load(filepath)
  108.         except:
  109.             filepathold = filepath+".old"
  110.             if (posixpath.isfile(filepathold) and
  111.                 posixpath.getsize(filepathold) > 0):
  112.                 # warning("Broken configuration file at %s" % filepath)
  113.                 # warning("Trying backup at %s" % filepathold)
  114.                 try:
  115.                     self._hardmap = self._backend.load(filepathold)
  116.                 except:
  117.                     raise PersistError("Broken configuration file at %s" %
  118.                                        filepathold)
  119.             else:
  120.                 raise PersistError("Broken configuration file at %s" %
  121.                                    filepath)
  122.  
  123.     def save(self, filepath=None):
  124.         """Save the persist to the given C{filepath}.
  125.  
  126.         If None is specified, then the filename passed during construction will
  127.         be used.
  128.         """
  129.         if filepath is None:
  130.             if self.filename is None:
  131.                 raise PersistError("Need a filename!")
  132.             filepath = self.filename
  133.         filepath = posixpath.expanduser(filepath)
  134.         if posixpath.isfile(filepath):
  135.             os.rename(filepath, filepath+".old")
  136.         dirname = posixpath.dirname(filepath)
  137.         if dirname and not posixpath.isdir(dirname):
  138.             os.makedirs(dirname)
  139.         self._backend.save(filepath, self._hardmap)
  140.  
  141.     def _traverse(self, obj, path, default=NOTHING, setvalue=NOTHING):
  142.         if setvalue is not NOTHING:
  143.             setvalue = self._backend.copy(setvalue)
  144.         queue = list(path)
  145.         marker = NOTHING
  146.         newobj = obj
  147.         while queue:
  148.             obj = newobj
  149.             elem = queue.pop(0)
  150.             newobj = self._backend.get(obj, elem)
  151.             if newobj is NotImplemented:
  152.                 if queue:
  153.                     path = path[:-len(queue)]
  154.                 raise PersistError("Can't traverse %r (%r): %r" %
  155.                                    (type(obj), path_tuple_to_string(path),
  156.                                     str(obj)))
  157.             if newobj is marker:
  158.                 break
  159.         if newobj is not marker:
  160.             if setvalue is not marker:
  161.                 newobj = self._backend.set(obj, elem, setvalue)
  162.         else:
  163.             if setvalue is marker:
  164.                 newobj = default
  165.             else:
  166.                 while True:
  167.                     if len(queue) > 0:
  168.                         if type(queue[0]) is int:
  169.                             newvalue = []
  170.                         else:
  171.                             newvalue = {}
  172.                     else:
  173.                         newvalue = setvalue
  174.                     newobj = self._backend.set(obj, elem, newvalue)
  175.                     if newobj is NotImplemented:
  176.                         raise PersistError("Can't traverse %r with %r" %
  177.                                            (type(obj), type(elem)))
  178.                     if not queue:
  179.                         break
  180.                     obj = newobj
  181.                     elem = queue.pop(0)
  182.         return newobj
  183.  
  184.     def _getvalue(self, path, soft=False, hard=False, weak=False):
  185.         if type(path) is str:
  186.             path = path_string_to_tuple(path)
  187.         marker = NOTHING
  188.         if soft:
  189.             value = self._traverse(self._softmap, path, marker)
  190.         elif hard:
  191.             value = self._traverse(self._hardmap, path, marker)
  192.         elif weak:
  193.             value = self._traverse(self._weakmap, path, marker)
  194.         else:
  195.             value = self._traverse(self._softmap, path, marker)
  196.             if value is marker:
  197.                 value = self._traverse(self._hardmap, path, marker)
  198.                 if value is marker:
  199.                     value = self._traverse(self._weakmap, path, marker)
  200.         return value
  201.  
  202.     def has(self, path, value=NOTHING, soft=False, hard=False, weak=False):
  203.         obj = self._getvalue(path, soft, hard, weak)
  204.         marker = NOTHING
  205.         if obj is marker:
  206.             return False
  207.         elif value is marker:
  208.             return True
  209.         result = self._backend.has(obj, value)
  210.         if result is NotImplemented:
  211.             raise PersistError("Can't check %r for containment" % type(obj))
  212.         return result
  213.  
  214.     def keys(self, path, soft=False, hard=False, weak=False):
  215.         obj = self._getvalue(path, soft, hard, weak)
  216.         if obj is NOTHING:
  217.             return []
  218.         result = self._backend.keys(obj)
  219.         if result is NotImplemented:
  220.             raise PersistError("Can't return keys for %s" % type(obj))
  221.         return result
  222.  
  223.     def get(self, path, default=None, soft=False, hard=False, weak=False):
  224.         value = self._getvalue(path, soft, hard, weak)
  225.         if value is NOTHING:
  226.             return default
  227.         return self._backend.copy(value)
  228.  
  229.     def set(self, path, value, soft=False, weak=False):
  230.         assert path
  231.         if type(path) is str:
  232.             path = path_string_to_tuple(path)
  233.         if soft:
  234.             map = self._softmap
  235.         elif weak:
  236.             map = self._weakmap
  237.         else:
  238.             self.assert_writable()
  239.             self._modified = True
  240.             map = self._hardmap
  241.         self._traverse(map, path, setvalue=value)
  242.  
  243.     def add(self, path, value, unique=False, soft=False, weak=False):
  244.         assert path
  245.         if type(path) is str:
  246.             path = path_string_to_tuple(path)
  247.         if soft:
  248.             map = self._softmap
  249.         elif weak:
  250.             map = self._weakmap
  251.         else:
  252.             self.assert_writable()
  253.             self._modified = True
  254.             map = self._hardmap
  255.         if unique:
  256.             current = self._traverse(map, path)
  257.             if type(current) is list and value in current:
  258.                 return
  259.         path = path+(sys.maxint,)
  260.         self._traverse(map, path, setvalue=value)
  261.  
  262.     def remove(self, path, value=NOTHING, soft=False, weak=False):
  263.         assert path
  264.         if type(path) is str:
  265.             path = path_string_to_tuple(path)
  266.         if soft:
  267.             map = self._softmap
  268.         elif weak:
  269.             map = self._weakmap
  270.         else:
  271.             self.assert_writable()
  272.             self._modified = True
  273.             map = self._hardmap
  274.         marker = NOTHING
  275.         while path:
  276.             if value is marker:
  277.                 obj = self._traverse(map, path[:-1])
  278.                 elem = path[-1]
  279.                 isvalue = False
  280.             else:
  281.                 obj = self._traverse(map, path)
  282.                 elem = value
  283.                 isvalue = True
  284.             result = False
  285.             if obj is not marker:
  286.                 result = self._backend.remove(obj, elem, isvalue)
  287.                 if result is NotImplemented:
  288.                     raise PersistError("Can't remove %r from %r" %
  289.                                        (elem, type(obj)))
  290.             if self._backend.empty(obj):
  291.                 if value is not marker:
  292.                     value = marker
  293.                 else:
  294.                     path = path[:-1]
  295.             else:
  296.                 break
  297.         return result
  298.  
  299.     def move(self, oldpath, newpath, soft=False, weak=False):
  300.         if not (soft or weak):
  301.             self.assert_writable()
  302.         if type(oldpath) is str:
  303.             oldpath = path_string_to_tuple(oldpath)
  304.         if type(newpath) is str:
  305.             newpath = path_string_to_tuple(newpath)
  306.         result = False
  307.         marker = NOTHING
  308.         value = self._getvalue(oldpath, soft, not (soft or weak), weak)
  309.         if value is not marker:
  310.             self.remove(oldpath, soft=soft, weak=weak)
  311.             self.set(newpath, value, weak, soft)
  312.             result = True
  313.         return result
  314.  
  315.     def root_at(self, path):
  316.         return RootedPersist(self, path)
  317.  
  318.  
  319. class RootedPersist(object):
  320.  
  321.     def __init__(self, parent, root):
  322.         self.parent = parent
  323.         if type(root) is str:
  324.             self.root = path_string_to_tuple(root)
  325.         else:
  326.             self.root = root
  327.  
  328.     readonly = property(lambda self: self.parent.readonly)
  329.     modified = property(lambda self: self.parent.modified)
  330.  
  331.     def assert_writable(self):
  332.         self.parent.assert_writable()
  333.  
  334.     def save(self):
  335.         self.parent.save()
  336.  
  337.     def has(self, path, value=NOTHING, soft=False, hard=False, weak=False):
  338.         if type(path) is str:
  339.             path = path_string_to_tuple(path)
  340.         return self.parent.has(self.root+path, value, soft, hard, weak)
  341.  
  342.     def keys(self, path, soft=False, hard=False, weak=False):
  343.         if type(path) is str:
  344.             path = path_string_to_tuple(path)
  345.         return self.parent.keys(self.root+path, soft, hard, weak)
  346.  
  347.     def get(self, path, default=None, soft=False, hard=False, weak=False):
  348.         if type(path) is str:
  349.             path = path_string_to_tuple(path)
  350.         return self.parent.get(self.root+path, default, soft, hard, weak)
  351.  
  352.     def set(self, path, value, soft=False, weak=False):
  353.         if type(path) is str:
  354.             path = path_string_to_tuple(path)
  355.         return self.parent.set(self.root+path, value, soft, weak)
  356.  
  357.     def add(self, path, value, unique=False, soft=False, weak=False):
  358.         if type(path) is str:
  359.             path = path_string_to_tuple(path)
  360.         return self.parent.add(self.root+path, value, unique, soft, weak)
  361.  
  362.     def remove(self, path, value=NOTHING, soft=False, weak=False):
  363.         if type(path) is str:
  364.             path = path_string_to_tuple(path)
  365.         return self.parent.remove(self.root+path, value, soft, weak)
  366.  
  367.     def move(self, oldpath, newpath, soft=False, weak=False):
  368.         if type(oldpath) is str:
  369.             oldpath = path_string_to_tuple(oldpath)
  370.         if type(newpath) is str:
  371.             newpath = path_string_to_tuple(newpath)
  372.         return self.parent.move(self.root+oldpath, self.root+newpath,
  373.                                 soft, weak)
  374.  
  375.     def root_at(self, path):
  376.         if type(path) is str:
  377.             path = path_string_to_tuple(path)
  378.         return self.parent.root_at(self.root+path)
  379.  
  380.  
  381. _splitpath = re.compile(r"(\[-?\d+\])|(?<!\\)\.").split
  382.  
  383. def path_string_to_tuple(path):
  384.     if "." not in path and "[" not in path:
  385.         return (path,)
  386.     result = []
  387.     tokens = _splitpath(path)
  388.     for token in tokens:
  389.         if token:
  390.             if token[0] == "[" and token[-1] == "]":
  391.                 try:
  392.                     result.append(int(token[1:-1]))
  393.                 except ValueError:
  394.                     raise PersistError("Invalid path index: %r" % token)
  395.             else:
  396.                 result.append(token.replace(r"\.", "."))
  397.     return tuple(result)
  398.  
  399.  
  400. def path_tuple_to_string(path):
  401.     result = []
  402.     for elem in path:
  403.         if type(elem) is int:
  404.             result[-1] += "[%d]" % elem
  405.         else:
  406.             result.append(str(elem).replace(".", "\."))
  407.     return ".".join(result)
  408.  
  409.  
  410. class Backend(object):
  411.  
  412.     def new(self):
  413.         raise NotImplementedError
  414.  
  415.     def load(self, filepath):
  416.         raise NotImplementedError
  417.  
  418.     def save(self, filepath, map):
  419.         raise NotImplementedError
  420.  
  421.     def get(self, obj, elem, _marker=NOTHING):
  422.         if type(obj) is dict:
  423.             newobj = obj.get(elem, _marker)
  424.         elif type(obj) in (tuple, list):
  425.             if type(elem) is int:
  426.                 try:
  427.                     newobj = obj[elem]
  428.                 except IndexError:
  429.                     newobj = _marker
  430.             elif elem in obj:
  431.                 newobj = elem
  432.             else:
  433.                 newobj = _marker
  434.         else:
  435.             newobj = NotImplemented
  436.         return newobj
  437.  
  438.     def set(self, obj, elem, value):
  439.         if type(obj) is dict:
  440.             newobj = obj[elem] = value
  441.         elif type(obj) is list and type(elem) is int:
  442.             lenobj = len(obj)
  443.             if lenobj <= elem:
  444.                 obj.append(None)
  445.                 elem = lenobj
  446.             elif elem < 0 and abs(elem) > lenobj:
  447.                 obj.insert(0, None)
  448.                 elem = 0
  449.             newobj = obj[elem] = value
  450.         else:
  451.             newobj = NotImplemented
  452.         return newobj
  453.  
  454.     def remove(self, obj, elem, isvalue):
  455.         result = False
  456.         if type(obj) is dict:
  457.             if elem in obj:
  458.                 del obj[elem]
  459.                 result = True
  460.         elif type(obj) is list:
  461.             if not isvalue and type(elem) is int:
  462.                 try:
  463.                     del obj[elem]
  464.                     result = True
  465.                 except IndexError:
  466.                     pass
  467.             elif elem in obj:
  468.                 obj[:] = [x for x in obj if x != elem]
  469.                 result = True
  470.         else:
  471.             result = NotImplemented
  472.         return result
  473.  
  474.     def copy(self, value):
  475.         if type(value) in (dict, list):
  476.             return copy.deepcopy(value)
  477.         return value
  478.  
  479.     def empty(self, obj):
  480.         return (not obj)
  481.  
  482.     def has(self, obj, elem):
  483.         contains = getattr(obj, "__contains__", None)
  484.         if contains:
  485.             return contains(elem)
  486.         return NotImplemented
  487.  
  488.     def keys(self, obj):
  489.         keys = getattr(obj, "keys", None)
  490.         if keys:
  491.             return keys()
  492.         elif type(obj) is list:
  493.             return range(len(obj))
  494.         return NotImplemented
  495.  
  496.  
  497. class PickleBackend(Backend):
  498.  
  499.     def __init__(self):
  500.         import cPickle
  501.         self._pickle = cPickle
  502.  
  503.     def new(self):
  504.         return {}
  505.  
  506.     def load(self, filepath):
  507.         file = open(filepath)
  508.         try:
  509.             return self._pickle.load(file)
  510.         finally:
  511.             file.close()
  512.  
  513.     def save(self, filepath, map):
  514.         file = open(filepath, "w")
  515.         try:
  516.             self._pickle.dump(map, file, 2)
  517.         finally:
  518.             file.close()
  519.  
  520.  
  521. class BPickleBackend(Backend):
  522.  
  523.     def __init__(self):
  524.         from checkbox.contrib import bpickle
  525.         self._bpickle = bpickle
  526.  
  527.     def new(self):
  528.         return {}
  529.  
  530.     def load(self, filepath):
  531.         file = open(filepath)
  532.         try:
  533.             return self._bpickle.loads(file.read())
  534.         finally:
  535.             file.close()
  536.  
  537.     def save(self, filepath, map):
  538.         file = open(filepath, "w")
  539.         try:
  540.             file.write(self._bpickle.dumps(map))
  541.         finally:
  542.             file.close()
  543.